#pragma once

#include <string>

// Google includes
#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include "Nodes/Defs.h"

class TestNode;
class TestNodeOr;
class TestNodeAnd;
class TestNodeNot;
class TestNodeAtom;

template<>
struct NodeTraits<TestNode>
{
    typedef TestNodeOr NodeOr;
    typedef TestNodeAnd NodeAnd;
    typedef TestNodeNot NodeNot;
    typedef TestNodeAtom NodeAtom;
};

class TestNode
{
	NodeType m_nodeType;

public:
	TestNode(NodeType type)
		: m_nodeType(type)
	{
	}

	// Mocked methods
    MOCK_CONST_METHOD0(getChildrenCount, size_t());
    MOCK_CONST_METHOD1(getChild, TestNode *(int));
    MOCK_CONST_METHOD1(compare, bool(TestNode *));

    // Methods to be overriden by subclasses
    virtual MOCK_METHOD1(accept, void(NodeVisitor<TestNode> *));

    // Own methods
    NodeType getNodeType() const
    {
    	return m_nodeType;
    }

    void overrideType(NodeType type)
    {
    	m_nodeType = type;
    }
};

class TestNodeOr : public TestNode
{
public:
	TestNodeOr()
		: TestNode(NT_OR)
	{
	}

    virtual void accept(NodeVisitor<TestNode> * pVisitor)
    {
        pVisitor->visitOr(this);
    }
};

class TestNodeAnd : public TestNode
{
public:
	TestNodeAnd()
		: TestNode(NT_AND)
	{
	}

    virtual void accept(NodeVisitor<TestNode> * pVisitor)
    {
        pVisitor->visitAnd(this);
    }
};

class TestNodeNot : public TestNode
{
public:
	TestNodeNot()
		: TestNode(NT_NOT)
	{
	}

    virtual void accept(NodeVisitor<TestNode> * pVisitor)
    {
        pVisitor->visitNot(this);
    }
};

class TestNodeAtom : public TestNode
{
	std::string m_text;

public:
	TestNodeAtom(const std::string & text)
		: TestNode(NT_ATOM)
		, m_text(text)
	{
	}

	std::string getAtomText() const
	{
		return m_text;
	}
    
    virtual void accept(NodeVisitor<TestNode> * pVisitor)
    {
        pVisitor->visitAtom(this);
    }
};

